1 Data
1.1 Input Data
# Ganti folder
path <- function() gsub ("\\\\", "/", readClipboard() )
setwd("C:/Users/Fathan/Documents/Obsidian Vault/2. Kuliah/Smt 6/Tugas TPM Aida")
# Baca Data
library(dplyr)
raw.data <- read.csv("Data.csv", sep=";") %>% #Baca csv
as.data.frame() # Ubah sebagai dataframe
data <- raw.data
# Tampilkan data
library(DT)
datatable(data)
1.2 Check Data
Cek tipe data
str(data)
## 'data.frame': 7385 obs. of 11 variables:
## $ CO2.Emissions.g.km. : int 196 221 136 255 244 230 232 255 267 212 ...
## $ Engine.Size.L. : int 2 2 2 4 4 4 4 4 4 2 ...
## $ Fuel.Consumption.City..L.100.km.: int 10 11 6 13 12 12 12 13 13 11 ...
## $ Fuel.Consumption.Hwy..L.100.km. : int 7 8 6 9 9 8 8 9 10 8 ...
## $ Fuel.Consumption.Comb..L.100.km.: int 9 10 6 11 11 10 10 11 12 9 ...
## $ Make : chr "ACURA" "ACURA" "ACURA" "ACURA" ...
## $ Model : chr "ILX" "ILX" "ILX HYBRID" "MDX 4WD" ...
## $ Vehicle.Class : chr "COMPACT" "COMPACT" "COMPACT" "SUV - SMALL" ...
## $ Cylinders : int 4 4 4 6 6 6 6 6 6 4 ...
## $ Transmission : chr "AS5" "M6" "AV7" "AS6" ...
## $ Fuel.Type : chr "Z" "Z" "Z" "Z" ...
Cek data hilang
colSums(is.na(data))
## CO2.Emissions.g.km. Engine.Size.L.
## 0 0
## Fuel.Consumption.City..L.100.km. Fuel.Consumption.Hwy..L.100.km.
## 0 0
## Fuel.Consumption.Comb..L.100.km. Make
## 0 0
## Model Vehicle.Class
## 0 0
## Cylinders Transmission
## 0 0
## Fuel.Type
## 0
Tidak ada data hilang pada setiap kolom.
1.3 Eksplorasi data
par(mfrow = c(2,6))
hist(data[,1], main="Y - Emisi CO2", cex.main=4)
hist(data[,2], main="X1 - Engine Size.L", cex.main=4)
barplot(table(data[,2]), main="X1 - Engine Size.L", cex.main=4)
hist(data[,3], main="X2 - Fuel Consum.L/km", cex.main=4)
hist(data[,4], main="X3 - Fuel Consum.L/km", cex.main=4)
hist(data[,5], main="X4 - Fuel Consum.L/km", cex.main=4)
barplot(table(data[,6]), main="X5 - Make", cex.main=4)
barplot(table(data[,7]), main="X6 - Model", cex.main=4)
barplot(table(data[,8]), main="X7 - Vehicle.Class", cex.main=4)
barplot(table(data[,9]), main="X8 - Cylinders", cex.main=4)
barplot(table(data[,10]), main="X9 - Transmission", cex.main=4)
barplot(table(data[,11]), main="X10 - Fuel.Type", cex.main=4)
1.3.1 X1 & X8
Dari sini kita bisa melihat bahwa X1 walaupun satuannya liter, namun bukan peubah kontinu. Ini bisa dilihat dari histogramnya yang penuh dnegan celah, mari kita lihat lebih dalam:
unique(data[,2])
## [1] 2 4 6 5 3 7 1 8
Terlihat bahwa jumlah nilai diskritnya terbatas dan sering mewakili kategori yang dibulatkan. Misalnya, mesin dengan ukuran sebenarnya 4.8 liter mungkin dikategorikan sebagai 5 liter.
Selain itu, nilai-nilai ini sering digunakan untuk mengklasifikasikan kendaraan ke dalam kelompok ukuran mesin tertentu, seperti mesin kecil, sedang, dan besar. Oleh karena itu, perlakuan data ini sebagai kategorik lebih tepat dalam beberapa analisis dan visualisasi, mencerminkan penggunaan praktis dalam klasifikasi ukuran mesin.
unique(data[,9])
## [1] 4 6 12 8 10 3 5 16
Begitu pula dengan X8 yakni banyaknya silinder, sehingga kedua peubah ini akan dijadikan sebagai peubah kategorik.
1.3.2 X5 - X9
Saking banyaknya kategori yang ada, bar chart yang dihasilkan nyaris nampak seperti histogram. Mari kita liat:
cat( "Jumlah baris =", nrow(data),
"\nBanyaknya kategori:",
"\nX5 (Make) =", length(unique(data[,6])),
"\nX6 (Model) =", length(unique(data[,7])),
"\nX7 (Vehicle.Class) =", length(unique(data[,8])),
"\nX9 (Transmission) =", length(unique(data[,10]))
)
## Jumlah baris = 7385
## Banyaknya kategori:
## X5 (Make) = 42
## X6 (Model) = 2053
## X7 (Vehicle.Class) = 16
## X9 (Transmission) = 27
Terlihat bahwa walaupun bar chart X6 terlihat sangat chaos, namun banyaknya kategori hanya 2000 saja., tidak sama dengan jumlah baris. Ini artinya X6 tidak semuanya unik. Ya sebenarnya bisa langsung dilihat dari tinggi rendahnya bar chart sih. Ini mah biar bisa liat begitu banyaknya kategori model pada X6 saja.
Akan tetapi, random forrest tidak mampu memodelkan lebih dari 53 kategori, sehingga alangkah baiknya jika X6 dikeluarkan dari analisis.
1.3.3 Y (Respon)
Mari kita lihat lebih dalam untuk sebaran data
library(ggplot2)
library(cowplot)
# Histogram
p1 <- ggplot(data, aes(x = `CO2.Emissions.g.km.`)) +
geom_histogram(binwidth = 20, fill = "#89b49e", color = "black") +
ylab("Frekuensi") + theme_void()
# Boxplot
p2 <- ggplot(data, aes(y = `CO2.Emissions.g.km.`)) +
geom_boxplot(fill = "#89b49e", color = "black", outlier.size = 3) +
xlab("Nilai") + theme_void() + coord_flip()
# Gabung plot
combined_plot <- plot_grid(p1, p2, ncol = 1, align = 'v',
rel_heights = c(1, 0.25))
# Menambahkan judul utama
title <- ggdraw() +
draw_label("Y - Emisi CO2", fontface = 'bold', x = 0.5, hjust = 0.5, size = 20)
# Menampilkan plot dengan judul utama
plot_grid(title, combined_plot, ncol = 1, rel_heights = c(0.1, 1))
Terlihat bahwa data menjulur ke kanan dan nampak memiliki banyak sekali outlier di sisi kanan.
1.4 Cleaning Data
Ubah tipe data
data <- data[,-7] %>% # Mengeluarkan X6
mutate_at(vars(2, 9), as.character) # Mengubah menjadi character
data <- data %>%
mutate_if(is.character, as.factor) # Mengubah char menjadi faktor
str(data)
## 'data.frame': 7385 obs. of 10 variables:
## $ CO2.Emissions.g.km. : int 196 221 136 255 244 230 232 255 267 212 ...
## $ Engine.Size.L. : Factor w/ 8 levels "1","2","3","4",..: 2 2 2 4 4 4 4 4 4 2 ...
## $ Fuel.Consumption.City..L.100.km.: int 10 11 6 13 12 12 12 13 13 11 ...
## $ Fuel.Consumption.Hwy..L.100.km. : int 7 8 6 9 9 8 8 9 10 8 ...
## $ Fuel.Consumption.Comb..L.100.km.: int 9 10 6 11 11 10 10 11 12 9 ...
## $ Make : Factor w/ 42 levels "ACURA","ALFA ROMEO",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ Vehicle.Class : Factor w/ 16 levels "COMPACT","FULL-SIZE",..: 1 1 1 12 12 3 3 3 3 1 ...
## $ Cylinders : int 4 4 4 6 6 6 6 6 6 4 ...
## $ Transmission : Factor w/ 27 levels "A10","A4","A5",..: 15 26 23 16 16 16 16 16 26 15 ...
## $ Fuel.Type : Factor w/ 5 levels "D","E","N","X",..: 5 5 5 5 5 5 5 5 5 5 ...
Dengan ini data sudah benar dan siap untuk dianalisis.
1.5 Split Data
library('caret')
set.seed(1401211016) # karena partisi data itu random, seperti sampling, maka perlu set.seed agar tidak berubah
# Membuat index partisi data
data.a <- data
colnames(data.a) <- c("Y","X1","X2","X3","X4","X5","X7","X8","X9","X10")
trainIndex <- createDataPartition(data.a$Y, # data Y
p = .8, # 80% data Train, 20% data Test
list = FALSE)
# Membagi data train dan test
TRAIN <- data.a[trainIndex, ] # ambil data train aja
TEST <- data.a[-trainIndex, ] # ambil bukan data train
2 Modeling
K-fold cross validation adalah salah satu teknik validasi untuk mencari tuning parameter terbaik sekaligus mengevaluasi kinerja model. Pada studi kasus ini digunakan 5-fold cross validation. Data dipartisi secara acak ke dalam lima subset data. Secara bergantian masing-masing subset akan dijadikan sebagai data testing, sementara empat subset data lainnya sebagai data training.
library('randomForest')
fitControl <- trainControl(
method = "cv", # metode K-fold cross validation
number = 5, # menggunakan 5-fold
returnResamp = "all")
2.1 Train model
2.1.1 Tuning
Parameter dengan tuneLength
Opsi tuneLength pada fungsi caret::train
akan memilih sejumlah tuning parameter atau kombinasi
tuning parameter yang dianggap paling tepat sesuai dengan
metode yang dipilih dan data training yang diberikan.
2.1.1.1 Cross Validation
library('randomForest')
rf <- train(Y ~ .,
data = TRAIN,
method = 'ranger',
tuneLength = 10, # mencoba 10 kombinasi tuning parameter
importance = "impurity",
trControl = fitControl, # K-fold cross validation
verbose = FALSE)
saveRDS(rf, file = "rf1.rds") # Save model di PC
rf
## Random Forest
##
## 5908 samples
## 9 predictor
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 4728, 4727, 4725, 4727, 4725
## Resampling results across tuning parameters:
##
## mtry splitrule RMSE Rsquared MAE
## 2 variance 26.059115 0.8917964 17.950466
## 2 extratrees 31.196727 0.8708193 23.809074
## 12 variance 6.264561 0.9889481 4.044398
## 12 extratrees 7.282406 0.9855205 4.915968
## 23 variance 5.069730 0.9925003 3.348303
## 23 extratrees 5.368454 0.9916653 3.496834
## 33 variance 4.839064 0.9931297 3.264002
## 33 extratrees 4.988263 0.9927318 3.299288
## 44 variance 4.552820 0.9939448 3.192870
## 44 extratrees 4.877054 0.9930225 3.232910
## 54 variance 4.728557 0.9934049 3.240143
## 54 extratrees 4.805850 0.9932157 3.211387
## 65 variance 4.707321 0.9934568 3.242472
## 65 extratrees 4.736722 0.9933976 3.200456
## 75 variance 4.704185 0.9934591 3.240592
## 75 extratrees 4.727946 0.9934194 3.198672
## 86 variance 4.709383 0.9934453 3.242050
## 86 extratrees 4.712715 0.9934574 3.193748
## 97 variance 4.726564 0.9933934 3.249581
## 97 extratrees 4.694268 0.9935061 3.194368
##
## Tuning parameter 'min.node.size' was held constant at a value of 5
## RMSE was used to select the optimal model using the smallest value.
## The final values used for the model were mtry = 44, splitrule = variance
## and min.node.size = 5.
Hasil validasi silang ditampilkan pada plot berikut:
plot(rf, main = "5-Fold Cross Validation Random Forest: tuneLength")
# Selain plot, bisa menggunakan bestTune
rf_best <- rf$bestTune
rf_best
## mtry splitrule min.node.size
## 9 44 variance 5
Berdasarkan output di atas, tuning parameter terbaik adalah
mtry = 44, splitrule = variance dan
min.node.size = 5, yang memberikan RMSE = 4.552820,
R-squared = 0.9939448 dan MAE = 3.192870.
2.1.1.2 Re-Fit Model Menggunakan Tuning Parameter Terbaik
Jadi setelah mencobakan kombinasi tuning parameter dan di dapatkan tuning, terbaik. Maka selanjutnya kita kan modeling dengan tuning parameter tersebut.
Re-fit model terhadap seluruh data testing dengan menggunakan tuning parameter terbaik yang diperoleh pada tahap sebelumnya:
rf <- train(Y ~ .,
data = TRAIN,
method = 'ranger',
tuneGrid = rf_best,
importance = "impurity",
verbose = FALSE)
saveRDS(rf, file = "rf2.rds")
rf
## Random Forest
##
## 5908 samples
## 9 predictor
##
## No pre-processing
## Resampling: Bootstrapped (25 reps)
## Summary of sample sizes: 5908, 5908, 5908, 5908, 5908, 5908, ...
## Resampling results:
##
## RMSE Rsquared MAE
## 4.817988 0.9931638 3.301318
##
## Tuning parameter 'mtry' was held constant at a value of 97
## Tuning
## parameter 'splitrule' was held constant at a value of extratrees
##
## Tuning parameter 'min.node.size' was held constant at a value of 5
rf_result <- rf$results
rf_result
## mtry splitrule min.node.size RMSE Rsquared MAE RMSESD
## 1 97 extratrees 5 4.817988 0.9931638 3.301318 0.2288119
## RsquaredSD MAESD
## 1 0.0006747375 0.06156657
Diperoleh model dengan RMSE = 4.817988, R-Squared = 0.9931638, MAE = 3.301318.
2.1.1.3 Evaluasi Terhadap Data Test
Untuk menguji kinerja model dalam memprediksi data baru, dilakukan evaluasi terhadap data testing:
library(MLmetrics)
eval_test_data <- function(model){
pred <- predict(model, newdata = TEST)
mae <- MAE(TEST$Y, pred)
rmse <- RMSE(TEST$Y, pred)
R2 <- R2_Score(TEST$Y, pred)
return(c(RMSE = rmse, R_Squared = R2, MAE = mae))
}
rf_eval <- eval_test_data(rf)
rf_eval
## RMSE R_Squared MAE
## 4.508533 0.994106 3.154478
Diperoleh model RMSE = 4.508533, R-Squared = 0.994106, MAE = 3.154478.
2.1.1.4 Variable Importance
plot(varImp(rf),
main = "Random Forest Variable Importance" )
Berdasarkan output di atas, tiga peubah terpenting adalah
X4, X2, dan X3.
2.1.2 Tuning
Parameter dengan tuneGrid
Opsi tuneGrid pada fungsi caret::train
memberikan keleluasaan kepada analis untuk menentukan kandidat
tuning parameter atau kombinasi tuning parameter yang
akan diuji.
2.1.2.1 Cross Validation
# untuk tuneGrid. Jadi kalo tuneLenght itu sangat random, hanya memasukkan angka saja. Sedangkan untuk tuneGrid itu bisa diatur untuk mtru, splitrule dan min.node.size nya
tg <- expand.grid(
mtry = seq(2, 14, 2), # mencoba kombinasi mtry
splitrule = c("variance","extratrees"),
min.node.size = c(5, 10, 20, 30))
rf_tg <- train(Y ~ .,
data = TRAIN,
method = 'ranger',
tuneGrid = tg,
ntree = 500, # banyaknya pohon
max_deep = 100, # maksimal kedalaman
importance = "impurity",
trControl = fitControl,
verbose = FALSE)
saveRDS(rf_tg, file = "rf_tg1.rds")
rf_tg
## Random Forest
##
## 5908 samples
## 9 predictor
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 4726, 4727, 4727, 4726, 4726
## Resampling results across tuning parameters:
##
## mtry splitrule min.node.size RMSE Rsquared MAE
## 2 variance 5 25.830164 0.8934379 17.812224
## 2 variance 10 26.073955 0.8923329 18.048965
## 2 variance 20 25.844875 0.8931036 17.842438
## 2 variance 30 26.104912 0.8900580 18.019345
## 2 extratrees 5 31.043809 0.8723268 23.777771
## 2 extratrees 10 30.904549 0.8715876 23.630935
## 2 extratrees 20 30.923607 0.8717697 23.645912
## 2 extratrees 30 31.071931 0.8716321 23.781352
## 4 variance 5 14.711726 0.9515277 9.680727
## 4 variance 10 14.773548 0.9511933 9.697261
## 4 variance 20 14.935770 0.9499941 9.764463
## 4 variance 30 15.080287 0.9489818 9.875269
## 4 extratrees 5 18.165992 0.9331870 13.482297
## 4 extratrees 10 18.285840 0.9322758 13.544688
## 4 extratrees 20 18.531676 0.9305220 13.679850
## 4 extratrees 30 18.368938 0.9315026 13.566974
## 6 variance 5 10.168935 0.9742071 6.641120
## 6 variance 10 10.267738 0.9737277 6.700751
## 6 variance 20 10.581552 0.9719700 6.854151
## 6 variance 30 10.911774 0.9702737 7.014029
## 6 extratrees 5 12.777425 0.9618376 9.343349
## 6 extratrees 10 12.918509 0.9609311 9.427109
## 6 extratrees 20 13.100203 0.9596644 9.472225
## 6 extratrees 30 13.506931 0.9571266 9.761551
## 8 variance 5 7.900230 0.9835605 5.212337
## 8 variance 10 8.096688 0.9827023 5.283437
## 8 variance 20 8.481752 0.9810353 5.497361
## 8 variance 30 8.954909 0.9789076 5.721117
## 8 extratrees 5 9.807478 0.9758535 7.019274
## 8 extratrees 10 10.056662 0.9746185 7.178155
## 8 extratrees 20 10.401438 0.9727993 7.370581
## 8 extratrees 30 10.971568 0.9698289 7.741080
## 10 variance 5 6.705075 0.9876887 4.452409
## 10 variance 10 6.852877 0.9871684 4.537926
## 10 variance 20 7.359380 0.9852425 4.786571
## 10 variance 30 7.827385 0.9833722 5.026027
## 10 extratrees 5 8.130569 0.9825692 5.735172
## 10 extratrees 10 8.380565 0.9815241 5.861804
## 10 extratrees 20 8.858418 0.9795298 6.154391
## 10 extratrees 30 9.428799 0.9768549 6.501314
## 12 variance 5 6.013781 0.9898478 4.037310
## 12 variance 10 6.217857 0.9891914 4.142780
## 12 variance 20 6.680086 0.9875902 4.378622
## 12 variance 30 7.208772 0.9856639 4.635387
## 12 extratrees 5 7.045223 0.9865077 4.879894
## 12 extratrees 10 7.304727 0.9855598 5.040823
## 12 extratrees 20 7.883341 0.9833140 5.403435
## 12 extratrees 30 8.460683 0.9809012 5.772302
## 14 variance 5 5.583719 0.9911311 3.766388
## 14 variance 10 5.799456 0.9904454 3.884107
## 14 variance 20 6.311742 0.9887875 4.146248
## 14 variance 30 6.764581 0.9872051 4.391941
## 14 extratrees 5 6.310069 0.9889568 4.346595
## 14 extratrees 10 6.616041 0.9878927 4.524314
## 14 extratrees 20 7.196900 0.9858214 4.883709
## 14 extratrees 30 7.769542 0.9836235 5.250298
##
## RMSE was used to select the optimal model using the smallest value.
## The final values used for the model were mtry = 14, splitrule = variance
## and min.node.size = 5.
plot(rf_tg, main = "5-Fold Cross Validation Random Forest: tuneGrid")
Lihat mana yang RMSE nya paling rendah, itu yang paling baik. Dari sini terlihat bahwa yang terbaik itu adalah variance.
rf_tg_best <- rf_tg$bestTune
rf_tg_best
## mtry splitrule min.node.size
## 49 14 variance 5
Berdasarkan output di atas, tuning parameter terbaik adalah
mtry = 14, splitrule = variance dan
min.node.size = 5, yang memberikan CV RMSE = 5.583719,
R-squared = 0.9911311, dan MAE = 3.766388.
2.1.2.2 Re-Fit Model Menggunakan Tuning Parameter Terbaik
Re-fit model terhadap seluruh data testing dengan menggunakan tuning parameter terbaik yang diperoleh pada tahap sebelumnya:
rf_tg <- train(Y ~ .,
data = TRAIN,
method = 'ranger',
tuneGrid = rf_tg_best,
ntree = 500,
max_deep = 100,
importance = "impurity",
verbose = FALSE)
saveRDS(rf_tg, file = "rf_tg2.rds")
rf_tg
## Random Forest
##
## 5908 samples
## 9 predictor
##
## No pre-processing
## Resampling: Bootstrapped (25 reps)
## Summary of sample sizes: 5908, 5908, 5908, 5908, 5908, 5908, ...
## Resampling results:
##
## RMSE Rsquared MAE
## 6.019447 0.9896652 3.920189
##
## Tuning parameter 'mtry' was held constant at a value of 14
## Tuning
## parameter 'splitrule' was held constant at a value of variance
##
## Tuning parameter 'min.node.size' was held constant at a value of 5
rf_tg_result <- rf_tg$results
rf_tg_result
## mtry splitrule min.node.size RMSE Rsquared MAE RMSESD
## 1 14 variance 5 6.019447 0.9896652 3.920189 0.4107795
## RsquaredSD MAESD
## 1 0.001200544 0.09668813
Diperoleh model dengan RMSE = 6.019447, R-Squared = 0.9896652 , MAE= 3.920189.
2.1.2.3 Evaluasi Terhadap Data Test
Untuk menguji kinerja model dalam memprediksi data baru, dilakukan evaluasi terhadap data testing:
rf_tg_eval <- eval_test_data(rf_tg)
rf_tg_eval
## RMSE R_Squared MAE
## 5.1912665 0.9919747 3.6345495
Diperoleh model RMSE = 5.1912665, R-Squared = 0.9919747, MAE = 3.6345495.
2.1.2.4 Variable Importance
plot(varImp(rf_tg),
main = "Random Forest Variable Importance")
Berdasarkan output di atas, tiga peubah terpenting adalah
X4, X2, dan X3.
3 Model terbaik
Sehingga model terbaiknya adalah model terakhir yakni
rf_tg_eval. Karena memiliki RMSE dan MAE terkecil juga
R-Squared yang terbesar.